package org.ycb.ycbisgod.ycbwoxihuanni.security;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import org.ycb.ycbisgod.ycbwoxihuanni.service.impl.UserDetailsServiceImpl;
import org.ycb.ycbisgod.ycbwoxihuanni.util.JWTUtil;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.security.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;

import java.io.IOException;

/**
 * JWT 认证过滤器。
 * 该过滤器会在 Spring Security 过滤器链中，在处理用户认证之前，
 * 拦截所有请求，尝试从请求头中解析 JWT Token，并根据 Token 设置 Spring Security 的认证信息。
 */
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JWTUtil jwtUtil;
    private final UserDetailsServiceImpl userDetailsService;

    /**
     * 构造函数注入 JWT 工具类和用户详情服务。
     * @param jwtUtil JWT 工具类
     * @param userDetailsService 用户详情服务
     */
    public JwtAuthenticationFilter(JWTUtil jwtUtil, UserDetailsServiceImpl userDetailsService) {
        this.jwtUtil = jwtUtil;
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        final String authorizationHeader = request.getHeader("Authorization");
        final String requestURI = request.getRequestURI();

        String jwt = null;
        Claims claims = null;

        // 检查 Authorization 头是否存在且以 "Bearer " 开头
        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            jwt = authorizationHeader.substring(7); // 提取 JWT Token

            try {
                // 尝试解析 JWT Token
                claims = jwtUtil.parseToken(jwt);
            } catch (ExpiredJwtException e) {
                // Token 已过期
                // 如果是刷新 Token 的请求，则放行让后续逻辑处理，否则返回 401 错误
                if (requestURI.equals("/user/refresh")) {
                    filterChain.doFilter(request, response);
                    return;
                } else {
                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // HTTP 401 Unauthorized
                    response.getWriter().write("JWT Token 已过期，请重新登录。");
                    return;
                }
            } catch (MalformedJwtException | SignatureException | UnsupportedJwtException | IllegalArgumentException e) {
                // Token 无效、签名错误、格式错误或非法参数
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // HTTP 401 Unauthorized
                response.getWriter().write("无效的 JWT Token。");
                return;
            }
        }

        // 如果 Claims 不为 null (表示 Token 解析成功)
        // 并且当前请求不是 /user/refresh (刷新 Token 的请求不需要设置认证信息，因为其 Token 可能已过期)
        // 并且 SecurityContextHolder 中没有认证信息 (防止重复设置)
        if (claims != null && !requestURI.equals("/user/refresh") && SecurityContextHolder.getContext().getAuthentication() == null) {
            // 从 Claims 中获取自定义的 'username' 字段
            String username = claims.get("username", String.class);

            if (username != null) {
                // 根据用户名加载用户详情
                UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);

                // 验证用户名并设置 Spring Security 认证信息
                if (username.equals(userDetails.getUsername())) {
                    // 创建 UsernamePasswordAuthenticationToken
                    UsernamePasswordAuthenticationToken authenticationToken =
                            new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    // 设置认证详情 (例如远程 IP 地址、会话 ID 等)
                    authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    // 将认证对象设置到 SecurityContextHolder 中，表示用户已认证
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }
            }
        }

        // 继续过滤器链
        // 如果上面成功设置了认证信息，Spring Security 的授权管理器将能够使用它来决定是否允许访问。
        // 如果请求是 permitAll 路径（如登录、注册），或者没有提供 Token，请求也会继续。
        filterChain.doFilter(request, response);
    }
}